package com.atguigu.gmall.payment.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeCloseRequest;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeCloseResponse;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.atguigu.gmall.common.rabbit.config.MqConst;
import com.atguigu.gmall.common.rabbit.util.RabbitService;
import com.atguigu.gmall.enums.model.OrderStatus;
import com.atguigu.gmall.enums.model.PaymentStatus;
import com.atguigu.gmall.enums.model.PaymentType;
import com.atguigu.gmall.order.client.OrderFeignClient;
import com.atguigu.gmall.order.model.OrderInfo;
import com.atguigu.gmall.payment.config.AlipayConfig;
import com.atguigu.gmall.payment.model.PaymentInfo;
import com.atguigu.gmall.payment.service.AlipayService;
import com.atguigu.gmall.payment.service.PaymentInfoService;
import com.atguigu.gmall.payment.strategy.PayApi;
import com.atguigu.gmall.payment.strategy.StrategyConfig;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;

/**
 * @author: atguigu
 * @create: 2023-06-27 16:00
 */
@Slf4j
@Service
public class AlipayServiceImpl implements AlipayService {

    @Autowired
    private OrderFeignClient orderFeignClient;

    @Autowired
    private PaymentInfoService paymentInfoService;

    @Autowired
    private AlipayClient alipayClient;

    @Autowired
    private RabbitService rabbitService;


    /**
     * 调用支付宝接口生成支付页面
     *
     * @param orderId
     * @return
     */
    @Override
    public String createPlipayPage(Long orderId) {
        try {
            //1.远程调用订单服务获取订单信息判断状态
            OrderInfo orderInfo = orderFeignClient.getOrderInfo(orderId);
            if (orderInfo != null && OrderStatus.UNPAID.name().equals(orderInfo.getOrderStatus())) {
                //2.新增本地交易记录
                PaymentInfo paymentInfo = new PaymentInfo();
                paymentInfo.setOutTradeNo(orderInfo.getOutTradeNo());
                paymentInfo.setOrderId(orderId);
                paymentInfo.setUserId(orderInfo.getUserId());
                paymentInfo.setPaymentType(PaymentType.ALIPAY.name());
                //paymentInfo.setTotalAmount(orderInfo.getTotalAmount());
                paymentInfo.setTotalAmount(new BigDecimal("0.01")); //TODO 测试阶段
                paymentInfo.setSubject(orderInfo.getTradeBody());
                paymentInfo.setPaymentStatus(PaymentStatus.UNPAID.name());
                paymentInfoService.savePaymentInfo(paymentInfo, PaymentType.ALIPAY.name());

                //3.调用支付宝SDK生成支付页面
                //3.1 创建统一下单支付页面请求对象
                AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
                //异步接收地址，仅支持http/https，公网可访问  支付宝平台主动调用支付系统告知支付结果
                request.setNotifyUrl(AlipayConfig.notify_payment_url);
                //同步跳转地址，仅支持http/https  用户支付后,显示支付成功页面
                request.setReturnUrl(AlipayConfig.return_payment_url);
                /******必传参数******/
                JSONObject bizContent = new JSONObject();
                //商户订单号，商家自定义，保持唯一性
                bizContent.put("out_trade_no", orderInfo.getOutTradeNo());
                //支付金额，最小值0.01元
                bizContent.put("total_amount", 0.01);
                //订单标题，不可使用特殊符号
                bizContent.put("subject", orderInfo.getTradeBody());
                //电脑网站支付场景固定传值FAST_INSTANT_TRADE_PAY
                bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
                //支付二维码有效期
                bizContent.put("timeout_express", "10m");
                request.setBizContent(bizContent.toString());
                //3.3 发请求调用支付宝得到支付表单
                AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
                if (response.isSuccess()) {
                    return response.getBody();
                }
            }
            throw new RuntimeException("订单状态不正确!");
        } catch (Exception e) {
            log.error("[支付服务]调用支付宝支付页面接口异常:{}", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 支付宝异步回调:通知商户系统支付结果
     *
     * @param paramsMap: 支付宝回调提交参数
     * @return
     */
    @Override
    public String paySuccessNotify(Map paramsMap) {
        try {
            //1. 验签:在支付系统验证提交数据是否为支付宝官方发出,以及在网格传输过程中是否被非法篡改过.避免"虚假"通知.
            boolean signVerified = AlipaySignature.rsaCheckV1(paramsMap, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); //调用SDK验证签名
            if (!signVerified) {
                // TODO 验签失败则记录异常日志，并在response中返回failure.
                log.error("[支付服务]支付回调验签异常:{}", paramsMap);
            } else {
                // TODO 验签成功后，按照支付结果异步通知中的描述，对支付结果中的业务内容进行二次校验，校验成功后在response中返回success并继续商户自身业务处理，校验失败返回failure
                //2. 验证订单以及订单金额是否一致(支付宝提交交易金额跟本地交易记录中金额一致)
                //2.1 获取提交参数中订单编号 查询本地交易记录
                String outTradeNo = (String) paramsMap.get("out_trade_no");
                PaymentInfo paymentInfo = paymentInfoService.getPaymentInfo(outTradeNo, PaymentType.ALIPAY.name());
                //3. 验证appId是否为商家的
                String totalAmount = (String) paramsMap.get("total_amount");
                if (paymentInfo.getTotalAmount().compareTo(new BigDecimal(totalAmount)) != 0) {
                    log.error("[支付服务]支付宝端交易金额跟本地交易记录不一致,本地校验记录:{},支付宝端:{}", paymentInfo, paramsMap);
                    return "failure";
                }
                //4. 验证支付宝端交易结果
                String tradeStatus = (String) paramsMap.get("trade_status");
                if ("TRADE_SUCCESS".equals(tradeStatus)) {
                    //5. 修改本地交易记录支付状态
                    String trade_no = (String) paramsMap.get("trade_no");
                    paymentInfo.setPaymentStatus(PaymentStatus.PAID.name());
                    paymentInfo.setCallbackTime(new Date());
                    paymentInfo.setCallbackContent(paramsMap.toString());
                    paymentInfo.setTradeNo(trade_no);
                    paymentInfoService.updateById(paymentInfo);

                    //6. 发送消息到MQ通知订单系统修改订单状态
                    rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_PAY, MqConst.ROUTING_PAYMENT_PAY, paymentInfo.getOrderId());
                    return "success";
                }
            }
        } catch (Exception e) {
            log.error("[支付系统],处理支付回调异常:{}", e);
            throw new RuntimeException(e);
        }
        return "failure";
    }

    /**
     * 退款
     *
     * @param orderId
     */
    @Override
    public void refund(Long orderId) {
        try {
            //1.根据订单ID查询订单信息判断状态
            OrderInfo orderInfo = orderFeignClient.getOrderInfo(orderId);
            if (orderInfo != null && OrderStatus.PAID.name().equals(orderInfo.getOrderStatus())) {
                //2.调用支付宝接口完成退款
                AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
                JSONObject bizContent = new JSONObject();
                bizContent.put("out_trade_no", orderInfo.getOutTradeNo());
                bizContent.put("refund_amount", 0.01);
                bizContent.put("out_request_no", "HZ01RF001");

                request.setBizContent(bizContent.toString());
                AlipayTradeRefundResponse response = alipayClient.execute(request);
                if (response.isSuccess()) {
                    String fundChange = response.getFundChange();
                    if ("Y".equals(fundChange)) {
                        //3.修改本地交易记录状态
                        LambdaUpdateWrapper<PaymentInfo> updateWrapper = new LambdaUpdateWrapper<>();
                        updateWrapper.set(PaymentInfo::getPaymentStatus, PaymentStatus.CLOSED.name());
                        updateWrapper.eq(PaymentInfo::getOrderId, orderId);
                        updateWrapper.eq(PaymentInfo::getPaymentType, PaymentType.ALIPAY.name());
                        paymentInfoService.update(updateWrapper);
                        //4.发送MQ消息通知订单服务修改交易记录状态
                        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL, MqConst.ROUTING_ORDER_CANCEL, orderId);
                    }
                }
            }
        } catch (AlipayApiException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 根据商户订单编号查询支付宝端交易状态
     *
     * @param outTradeNo
     * @return
     */
    @Override
    public String getAliPaySatus(String outTradeNo) {
        try {
            AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
            JSONObject bizContent = new JSONObject();
            //商户订单号，商家自定义，保持唯一性
            bizContent.put("out_trade_no", outTradeNo);
            request.setBizContent(bizContent.toString());
            AlipayTradeQueryResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                return response.getTradeStatus();
            }
        } catch (Exception e) {
            log.error("[支付服务]查询支付宝交易异常,订单ID:{}, 异常信息:", outTradeNo, e);
            throw new RuntimeException(e);
        }
        return null;
    }

    /**
     * 关闭支付宝交易订单
     *
     * @param outTradeNo
     */
    @Override
    public void closeAlipay(String outTradeNo) {
        try {
            AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", outTradeNo);
            request.setBizContent(bizContent.toString());
            AlipayTradeCloseResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                log.info("关闭支付宝交易成功:{}", response.toString());
            }
        } catch (Exception e) {
            log.error("[支付服务]关闭支付宝交易异常,订单ID:{}, 异常信息:", outTradeNo, e);
            throw new RuntimeException(e);
        }
    }


    @Autowired
    private StrategyConfig payStrategy;


    @Override
    public Object payPage(String payType, Long orderId) {
        PayApi payApi = payStrategy.getStrategyByType(payType);
        return payApi.payPage(payType, orderId);
    }
}
